package org.wisteria.framework;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.wisteria.framework.core.app.AppModuleLoader;
import org.wisteria.framework.core.cfg.ConfigItem;
import org.wisteria.framework.core.cfg.ConfigMetadata;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.*;

/**
 * 启动类
 */
@SpringBootApplication(scanBasePackages = {"org.wisteria.framework", "${wisteria.modules.basePackages:}"})
public class WisteriaSinensisApplication {

	private static Logger logger = LoggerFactory.getLogger(WisteriaSinensisApplication.class);
	
	public static void main(String[] args) throws Exception {
        args = convert2Args(collectMeatadata());

        printMetadata(args);
		SpringApplication.run(WisteriaSinensisApplication.class, args);
		logger.info("应用启动成功");
	}

	private static String[] convert2Args(Properties properties) {
        String[] ret = new String[properties.size()];
        Set<Object> keys = properties.keySet();
        int idx = 0;
        for (Object key : keys) {
            String cfgValue = properties.get(key).toString();
            String arg = "";
            if (StringUtils.hasText(cfgValue)) {
                arg = "--" + key.toString() + "=" + cfgValue;
            }
            ret[idx++] = arg;
        }
        return ret;
    }

    private static void printMetadata(String[] args) {
        if (args == null || args.length <= 0) {
            return;
        }

        int length = args.length;
        for (int i = 0; i < length - 1; i++) {
            for (int j = 0; j < length - 1 - i; j++) {
                if (args[j].compareTo(args[j + 1]) > 0) {
                    String temp = args[j];
                    args[j] = args[j+1];
                    args[j+1] = temp;
                }
            }
        }

        StringBuffer sb = new StringBuffer("=====================应用实例配置=====================\n");
        for (String str : args) {
            if (!StringUtils.hasText(str)) {
                continue;
            }
            sb.append(str).append("\n");
        }
        logger.info(sb.toString());
    }

    private static Properties collectMeatadata() throws Exception {
        Properties configProperties = collectConfigMetadata();
        AppModuleLoader appModuleLoader = new AppModuleLoader();
        mergeProerties(configProperties, collectApplicationMetadata(), appModuleLoader.scan());
        return configProperties;
    }

    private static void mergeProerties(Properties src, Properties... dest) {
	    if (dest == null || dest.length <= 0) {
	        return;
        }

        for (Properties properties : dest) {
            Set<Object> configKeys = properties.keySet();
            if (!CollectionUtils.isEmpty(configKeys)) {
                for (Object key : configKeys) {
                    String strKey = (String)key;
                    src.setProperty(strKey, properties.getProperty(strKey));
                }
            }
        }
    }

    private static Properties collectApplicationMetadata() throws Exception {
        logger.info("从[application.properties]读取模块配置元数据...");
        Properties properties = new Properties();
        ClassPathResource classPathResource = new ClassPathResource("application.properties");
        InputStream is = classPathResource.getInputStream();
        properties.load(is);
        is.close();
        return properties;
    }

    private static Properties collectConfigMetadata() throws Exception {
        Map<String, ConfigMetadata> configMetadataMap = new HashMap();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath*:META-INF/wisteria-config-metadata*.json");
        if (resources != null && resources.length > 0) {
            for (Resource resource : resources) {
                logger.info("从[" + resource.getFilename() + "]读取模块配置元数据...");

                InputStream is = resource.getInputStream();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int nRead;
                while ((nRead = is.read(buffer)) > 0) {
                    baos.write(buffer, 0, nRead);
                    baos.flush();
                }
                ConfigMetadata configMetadata = JSON.parseObject(baos.toByteArray(), ConfigMetadata.class);
                putConfigMetadata(configMetadataMap, configMetadata);
                is.close();
                baos.close();
            }
        }

        Properties properties = new Properties();
        if (!CollectionUtils.isEmpty(configMetadataMap)) {
            for (Map.Entry<String, ConfigMetadata> entry : configMetadataMap.entrySet()) {
                List<ConfigItem> configItems = entry.getValue().getItems();
                if (CollectionUtils.isEmpty(configItems)) {
                    continue;
                }

                for (ConfigItem configItem : configItems) {
                    properties.setProperty(configItem.getKey(), configItem.getDefaultValue());
                }
            }
        }

        return properties;
    }

    private static void putConfigMetadata(Map<String, ConfigMetadata> configMetadataMap, ConfigMetadata configMetadata) {
        String groupName = configMetadata.getGroup();
        if (!configMetadataMap.containsKey(groupName)) {
            configMetadataMap.put(groupName, configMetadata);
            return;
        }

        ConfigMetadata exists = configMetadataMap.get(groupName);
        for (ConfigItem item : configMetadata.getItems()) {
            exists.addItem(item);
        }
    }

}
